package ar.com.jsejtman.cajugastos2.core.service.data;

import java.util.ArrayList;
import java.util.List;

/**
 * Define una página de datos estándar. Es necesaria para trabajar búsquedas de información con
 * paginación.
 * 
 * @author Sebastián Matienzo
 * 
 * @param <E> Entidad de negocio a la que corresponde esta página de datos.
 */
public class DataPage<E extends Object> {

	/**
	 * Tamaño de la página de datos actual.
	 */
	private Integer pageSize;

	/**
	 * Número actual de la página de datos actual.
	 */
	private Integer currentPageNumber;

	/**
	 * Cantidad total de datos (NO de páginas).
	 */
	private Integer totalResults;

	/**
	 * Datos correspondiente a la página actual.
	 */
	private List<E> data;

	/**
	 * Constructor por defecto: inicializa todos los valores asumiendo que es una página sin
	 * resultados.
	 */
	public DataPage() {

		this.setTotalResults(0);
		this.setCurrentPageNumber(1);
		this.setPageSize(0);
		this.setData(new ArrayList<E>());
	}

	/**
	 * Obtiene el tamaño de la página.
	 * 
	 * @return Tamaño de la página.
	 */
	public final Integer getPageSize() {

		return (this.pageSize);
	}

	/**
	 * Asigna el tamaño de la página.
	 * 
	 * @param pageSize Tamaño de la página.
	 */
	public final void setPageSize(final Integer pageSize) {

		this.pageSize = pageSize;
	}

	/**
	 * Obtiene el número actual de la página.
	 * 
	 * @return Número actual de la página.
	 */
	public final Integer getCurrentPageNumber() {

		return (this.currentPageNumber);
	}

	/**
	 * Asigna el número actual de la página.
	 * 
	 * @param currentPageNumber Número actual de la página.
	 */
	public final void setCurrentPageNumber(final Integer currentPageNumber) {

		this.currentPageNumber = currentPageNumber;
	}

	/**
	 * Obtiene la cantidad total de datos.
	 * 
	 * @return Cantidad total de datos.
	 */
	public final Integer getTotalResults() {

		return (this.totalResults);
	}

	/**
	 * Asigna la cantidad total de datos.
	 * 
	 * @param totalResults Cantidad total de datos.
	 */
	public final void setTotalResults(final Integer totalResults) {

		this.totalResults = totalResults;
	}

	/**
	 * Obtiene la página actual de datos.
	 * 
	 * @return página actual de datos.
	 */
	public final List<E> getData() {

		return (this.data);
	}

	/**
	 * Asigna la página actual de datos.
	 * 
	 * @param data página actual de datos.
	 */
	public final void setData(final List<E> data) {

		this.data = data;
	}

	/**
	 * Verifica si es la primera página del total de datos disponibles.
	 * 
	 * @return True si lo es; caso contrario, False.
	 */
	public final boolean isFirstPage() {

		if (this.data == null || this.data.isEmpty()) {
			return (true);
		}

		return (this.currentPageNumber == 1);
	}

	/**
	 * Verifica si es la última página del total de datos disponibles.
	 * 
	 * @return True si lo es; caso contrario, False.
	 */
	public final boolean isLastPage() {

		if (this.data == null || this.data.isEmpty()) {
			return (true);
		}

		return (this.getLastPageNumber() == this.currentPageNumber);
	}

	/**
	 * Obtiene el número de posición al que corresponde el primer elemento de la página, evaluado
	 * dentro del total de elementos disponibles.
	 * 
	 * @return Número de posición del primer elemento de la página.
	 */
	public final Integer getStartingOffset() {

		if (this.data == null || this.data.isEmpty()) {
			return (0);
		}

		if (this.hasInvalidCurrentPageNumber() || this.hasInvalidPageSize()) {
			return (0);
		}

		return (((this.currentPageNumber - 1) * this.pageSize) + 1);
	}

	/**
	 * Obtiene el número de posición al que corresponde el último elemento de la página, evaluado
	 * dentro del total de elementos disponibles.
	 * 
	 * @return Número de posición del último elemento de la página.
	 */
	public final Integer getEndingOffset() {

		if (this.data == null || this.data.isEmpty()) {
			return (0);
		}

		if (this.hasInvalidCurrentPageNumber() || this.hasInvalidPageSize()) {
			return (0);
		}

		return (((this.currentPageNumber - 1) * this.pageSize) + this.data.size());
	}

	/**
	 * Obtiene el número de la última página.
	 * 
	 * @return Número de la última página.
	 */
	public final Integer getLastPageNumber() {

		if (this.hasInvalidTotalResults() || this.hasInvalidPageSize()) {
			return (1);
		}

		return (this.totalResults / this.pageSize + ((this.totalResults % this.pageSize > 0) ? 1
		        : 0));
	}

	/**
	 * Indica si el número de página actual es inválido.
	 * 
	 * @return True si el número de página actual es inválido; caso contrario, False.
	 */
	private boolean hasInvalidCurrentPageNumber() {

		return (this.currentPageNumber == null || this.currentPageNumber < 1);
	}

	/**
	 * Indica si el tamaño de página es inválido.
	 * 
	 * @return True si el tamaño de página es inválido; caso contrario, False.
	 */
	private boolean hasInvalidPageSize() {

		return (this.pageSize == null || this.pageSize < 1);
	}

	/**
	 * Indica si la cantidad total de resultados es inválida.
	 * 
	 * @return True si la cantidad total de resultados es inválida; caso contrario, False.
	 */
	private boolean hasInvalidTotalResults() {

		return (this.totalResults == null || this.totalResults < 1);
	}
}
